package com.ZeroCarbon.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.ZeroCarbon.common_service.CommonService;
import com.ZeroCarbon.domain.dto.*;
import com.ZeroCarbon.domain.po.Comment;
import com.ZeroCarbon.domain.po.Post;
import com.ZeroCarbon.domain.po.UserPost;
import com.ZeroCarbon.entity.PageObject;
import com.ZeroCarbon.exception.BusinessLogicException;
import com.ZeroCarbon.mapper.PostMapper;
import com.ZeroCarbon.service.IPostService;
import com.ZeroCarbon.service.IUserFavoriteService;
import com.ZeroCarbon.service.IUserPostService;
import com.ZeroCarbon.utils.BusinessMethodUtils;
import com.ZeroCarbon.utils.UserHolder;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static com.ZeroCarbon.exception.BusinessLogicException.BUSY_EXCEPTION;
import static com.ZeroCarbon.exception.BusinessLogicException.ERROR_EXCEPTION;
import static com.ZeroCarbon.prefix.MySqlKey.POST_PREFIX;
import static com.ZeroCarbon.prefix.MySqlKey.generateId;
import static com.ZeroCarbon.prefix.RedisKey.*;

/**
 * 记录信息实现类
 *
 * @since 2024-10-26
 */
@Service
@RequiredArgsConstructor
public class PostServiceImpl extends ServiceImpl<PostMapper, Post> implements IPostService {

    private final StringRedisTemplate redis;

    private final IUserPostService userPostService;

    private final IUserFavoriteService userFavoriteService;

    private CommonService<Post> serviceUtils;

    @Autowired
    public void initServiceUtils(PostMapper postMapper) {
        serviceUtils = new CommonService<>(postMapper);
    }

    /**
     * 保存记录
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void savePost(PostReceiverDTO postReceiverDTO) throws BusinessLogicException {
        //业务是否成功标记
        boolean flag;
        //获取用户id
        String userId = UserHolder.getUser();
        //转化为post对象
        Post post = postReceiverDTO.toPost();
        //随机生成id
        String randomId = generateId(POST_PREFIX);
        //设置id
        post.setId(randomId);
        try {
            //保存到用户记录关系表
            flag = Db.save(new UserPost(userId, randomId));
            //保存帖子
            flag = flag && save(post);
        } catch (Exception e) {
            throw new BusinessLogicException(ERROR_EXCEPTION);
        }
        //没有保存成功的情况
        if (!flag) {
            throw new BusinessLogicException(ERROR_EXCEPTION);
        }
    }

    /**
     * 获取帖子
     *
     * @param type       0表示随机推荐，1表示热度较高，2表示最新发布
     * @param isFavorite 是否获取收藏记录
     */
    @Override
    public List<PostPreviewDTO> getRandomPost(int type, boolean isFavorite) throws BusinessLogicException {
        //获取当前用户的id
        String userId = UserHolder.getUser();
        //记录收藏集合
        List<Post> posts;
        //帖子构造器
        LambdaQueryWrapper<Post> postWrapper = new LambdaQueryWrapper<Post>()
                .eq(Post::isPrivateCondition, 0);
        //设置排序规则
        if (type == 0) {
            //点赞和时间高到低
            postWrapper.orderByDesc(Post::getLiked).orderByDesc(Post::getLastTime);
        } else if (type == 1) {
            //点赞高到低
            postWrapper.orderByDesc(Post::getLiked);
        } else {
            //时间高到低
            postWrapper.orderByDesc(Post::getLastTime);
        }
        //获取收藏的帖子
        if (isFavorite) {
            if (userId == null) {
                return Collections.emptyList();
            }
            //当前用户收藏的帖子id
            List<String> userFavoriteCollection = userFavoriteService.collection();
            //获取帖子内容
            posts = userFavoriteCollection.stream()
                    .map(this::getById)
                    .collect(Collectors.toList());
        } else {
            //主页获取帖子
            posts = list(postWrapper);
        }
        //如果为空，则返回空集合
        if (posts == null || posts.isEmpty()) {
            return Collections.emptyList();
        }
        //处理数据
        return getPostPreviewDTOList(posts, userId);
    }

    /**
     * 点赞记录
     *
     * @return false取消点赞，true点赞
     */
    @Override
    public Integer thumbsUpPost(String id) throws BusinessLogicException {
        //用户id
        String userId = UserHolder.getUser();
        //点赞帖子id
        String key = THUMB_POST + id;
        //帖子点赞数构造器
        LambdaQueryWrapper<Post> likedWrapper = new LambdaQueryWrapper<Post>()
                .select(Post::getLiked)
                .eq(Post::getId, id);
        //已点赞再次点击则取消赞
        int step = BusinessMethodUtils.isExist(key, userId) ? -1 : 1;
        //更新点赞
        Boolean isSuccess = serviceUtils.updateSqlIncrementOrCheck("liked", step, id);
        //修改redis中的记录
        if (step > 0 && isSuccess) {
            redis.opsForSet().add(key, userId);
        } else {
            redis.opsForSet().remove(key, userId);
        }
        try {
            //获取当前点赞数
            return getOne(likedWrapper).getLiked();
        } catch (Exception e) {
            throw new BusinessLogicException(ERROR_EXCEPTION);
        }
    }

    /**
     * 收藏记录
     *
     * @param id 记录id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer collectPost(String id) throws BusinessLogicException {
        //获取用户id
        String userId = UserHolder.getUser();
        //获取用户收藏key
        String key = COLLECT_POST + id;
        //收藏构建器
        LambdaQueryWrapper<Post> collectWrapper = new LambdaQueryWrapper<Post>()
                .select(Post::getFavorite).eq(Post::getId, id);
        //已点赞再次点击则取消赞
        int step = BusinessMethodUtils.isExist(key, userId) ? -1 : 1;
        //更新收藏
        Boolean isSuccess = serviceUtils.updateSqlIncrementOrCheck("favorite", step, id);
        //修改redis中的记录
        if (step > 0 && isSuccess) {
            redis.opsForSet().remove(key, userId);
            //收藏记录
            userFavoriteService.collectPost(id);
        } else {
            redis.opsForSet().add(key, userId);
            //移除记录
            userFavoriteService.unCollect(id);
        }
        try {
            //获取当前收藏数
            return getOne(collectWrapper).getLiked();
        } catch (Exception e) {
            throw new BusinessLogicException(ERROR_EXCEPTION);
        }
    }

    /**
     * 判断记录是否被收藏
     *
     * @param id 记录id
     */
    @Override
    public Boolean isFavorite(String id) {
        //获取用户id
        String userId = UserHolder.getUser();
        // 如果是未登录状态，直接返回未点赞状态
        if (userId == null) {
            return false;
        }
        //获取收藏帖子key
        String key = COLLECT_POST + id;
        //判断记录是否被用户收藏
        return BusinessMethodUtils.isExist(key, userId);
    }

    /**
     * 获取记录详细信息
     *
     * @param id 记录id
     */
    @Override
    public PostDetailDTO getDetailPost(String id) throws BusinessLogicException {
        //获取帖子
        Post post = getById(id);
        //转化为PostDetailDTO对象
        PostDetailDTO postDetailDTO = post.toPostDetailDTO();
        //获取用户id
        String userId = UserHolder.getUser();
        //如果用户id不为空则设置额外信息
        if (userId != null) {
            //点赞key
            String thumbKey = THUMB_POST + id;
            //收藏key
            String collectKey = COLLECT_POST + id;
            //历史记录key
            String key = HISTORY_POST + userId;
            //将记录id添加进用户观看历史
            redis.opsForSet().add(key, id);
            //判断用户是否点赞
            postDetailDTO.setFavorite(BusinessMethodUtils.isExist(thumbKey, userId))
                    .setCollection(BusinessMethodUtils.isExist(collectKey, userId));
        }
        //获取用户名和头像信息
        UsernameAndAvatarDTO usernameAndAvatarDTO = userPostService.getAvatarAndUsername(id);
        //记录评论
        List<Comment> comments;
        try {
            //获取评论信息
            comments = Db.lambdaQuery(Comment.class).eq(Comment::getRecordId, id).list();
        } catch (Exception e) {
            throw new BusinessLogicException(e.getMessage());
        }
        //设置额外信息
        return postDetailDTO.setAvatar(usernameAndAvatarDTO.getAvatar())
                .setUsername(usernameAndAvatarDTO.getUsername())
                .setComments(comments);
    }

    /**
     * 获取当前帐号的帖子
     */
    @Override
    public PageObject getCurrentPosts(int page, int size) throws BusinessLogicException {
        //创建分页对象
        Page<UserPost> postPage = new Page<>(page, size);
        //获取用户id
        String userId = UserHolder.getUser();
        Page<UserPost> userPostPage;
        try {
            //获取属于当前用户所有记录id
            userPostPage = Db.lambdaQuery(UserPost.class)
                    .eq(UserPost::getId, userId)
                    .orderByAsc(UserPost::getRecordId)
                    .page(postPage);
        } catch (Exception e) {
            throw new BusinessLogicException(ERROR_EXCEPTION);
        }
        //获取查询记录
        List<UserPost> currentPosts = userPostPage.getRecords();
        //判断当前帖子是否为空
        if (currentPosts.isEmpty()) {
            return new PageObject(Collections.emptyList(), page, 0, size, 0);
        }
        //处理数据
        List<PostManageDTO> postsManage = currentPosts.stream()
                .map(userPost -> {
                    //获取记录id
                    String recordId = userPost.getRecordId();
                    //获取帖子构建器
                    LambdaQueryWrapper<Post> postWrapper = new LambdaQueryWrapper<Post>()
                            .eq(Post::getId, recordId);
                    Post post = null;
                    try {
                        //获取帖子数据
                        post = getOne(postWrapper);
                    } catch (Exception e) {
                        throw new RuntimeException(ERROR_EXCEPTION);
                    }
                    //转化对象
                    return post.toPostManageDTO();
                }).collect(Collectors.toList());
        //返回分页结果
        return new PageObject(postsManage, page, userPostPage.getTotal(), size, userPostPage.getPages());
    }

    /**
     * 获取编辑记录
     *
     * @param id 记录id
     */
    @Override
    public PostEditDTO getEditPost(String id) throws BusinessLogicException {
        //获取详细属性然后拷贝部分属性
        return BeanUtil.copyProperties(getDetailPost(id), PostEditDTO.class);
    }

    /**
     * 更新编辑记录
     */
    @Override
    public void updateEditPost(PostEditDTO postEditDTO) throws BusinessLogicException {
        //帖子更新构造器
        LambdaQueryWrapper<Post> updatePostWrapper = new LambdaQueryWrapper<Post>()
                .eq(Post::getId, postEditDTO.getId());
        //转化为Post对象
        Post post = postEditDTO.toPost();
        try {
            //更新结果
            if (!update(post, updatePostWrapper)) {
                throw new BusinessLogicException();
            }
        } catch (BusinessLogicException e) {
            throw new BusinessLogicException(ERROR_EXCEPTION);
        }
    }

    /**
     * 删除记录
     */
    @Override
    public void deletePost(String id) throws BusinessLogicException {
        try {
            //删除失败的情况
            if (!removeById(id)) {
                throw new BusinessLogicException();
            }
        } catch (BusinessLogicException e) {
            throw new BusinessLogicException(ERROR_EXCEPTION);
        }
    }

    /**
     * 获取当前用户的浏览记录
     */
    @Override
    public List<PostPreviewDTO> getUserHistory() throws BusinessLogicException {
        //获取用户id
        String userId = UserHolder.getUser();
        //历史记录key
        String key = HISTORY_POST + userId;
        //记录id集合
        Set<String> recordIds;
        try {
            //根据key获取id集合
            recordIds = redis.opsForSet().members(key);
        } catch (Exception e) {
            throw new BusinessLogicException(ERROR_EXCEPTION);
        }
        //如果没有浏览过帖子的情况
        if (recordIds == null || recordIds.isEmpty()) {
            return Collections.emptyList();
        }
        //获取帖子
        List<Post> historyPostList = recordIds.stream()
                .map(this::getById)
                .collect(Collectors.toList());
        //转化对象
        return getPostPreviewDTOList(historyPostList, userId);
    }

    /**
     * 搜索帖子
     */
    @Override
    public List<PostPreviewDTO> searchPost(String search) throws BusinessLogicException {
        //获取用户id
        String userId = UserHolder.getUser();
        //搜索构建器
        LambdaQueryWrapper<Post> searchWrapper = new LambdaQueryWrapper<Post>()
                .like(Post::getTitle, search)
                .or()
                .like(Post::getTagsJson, search)
                .or()
                .like(Post::getContent, search);
        //记录搜索结果
        List<Post> searchPostList;
        try {
            //获取搜索结果
            searchPostList = list(searchWrapper);
        } catch (Exception e) {
            throw new BusinessLogicException(ERROR_EXCEPTION);
        }
        //返回结果
        return getPostPreviewDTOList(searchPostList, userId);
    }

    /**
     * Post类型列表转化为PostPreviewDTO类型列表
     */
    private List<PostPreviewDTO> getPostPreviewDTOList(List<Post> posts, String userId) {
        return posts.stream()
                .map(post -> {
                    //转化为预览对象
                    PostPreviewDTO postPreviewDTO = post.toPostPreviewDTO();
                    //如果用户id不为空则设置用户的喜好状态
                    if (userId != null) {
                        //获取id
                        String key = THUMB_POST + post.getId();
                        //设置收藏状态
                        postPreviewDTO.setFavorite(BusinessMethodUtils.isExist(key, userId));
                    }
                    //获取帖子id
                    String postId = Db.lambdaQuery(UserPost.class)
                            .eq(UserPost::getRecordId, post.getId())
                            .one()
                            .getRecordId();
                    //记录用户名和头像
                    UsernameAndAvatarDTO avatarAndUsername;
                    try {
                        //获取帖子id对应的用户名和头像
                        avatarAndUsername = userPostService.getAvatarAndUsername(postId);
                    } catch (BusinessLogicException e) {
                        throw new RuntimeException(BUSY_EXCEPTION);
                    }
                    //设置并返回结果
                    return postPreviewDTO.setUsername(avatarAndUsername.getUsername())
                            .setAvatar(avatarAndUsername.getAvatar());
                }).collect(Collectors.toList());
    }
}
